home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / sml_nj / cml-098.lha / cml-0.9.8 / src / trace-cml.sml < prev   
Encoding:
Text File  |  1993-02-09  |  9.5 KB  |  328 lines

  1. (* trace-cml.sml
  2.  *
  3.  * COPYRIGHT (c) 1992 AT&T Bell Laboratories
  4.  *
  5.  * This module provides rudimentary debugging support in the form of mechanisms
  6.  * to control debugging output, and to monitor thread termination.  This
  7.  * version of this module is adapted from Cliff Krumvieda's utility for tracing
  8.  * CML programs.  It provides three facilities: trace modules, for controlling
  9.  * debugging output; thread watching, for detecting thread termination; and
  10.  * a mechanism for reporting uncaught exceptions on a per thread basis.
  11.  *)
  12.  
  13. functor TraceCML (
  14.     structure CML : INTERNAL_CML
  15.           and RunCML : RUN_CML
  16.       and CIO : CONCUR_IO
  17.     sharing CML = RunCML.CML = CIO.CML
  18.     sharing type CML.thread_id = CML.CMLBase.thread_id
  19.   ) : TRACE_CML = struct
  20.  
  21.     open CML (* need to open INTERNAL_CML version before rebinding CML *)
  22.  
  23.     structure CIO : CONCUR_IO = CIO
  24.     structure CML : CONCUR_ML = CML
  25.  
  26.   (* where to direct trace output to *)
  27.     datatype trace_to
  28.       = TraceToOut
  29.       | TraceToErr
  30.       | TraceToNull
  31.       | TraceToFile of string
  32.       | TraceToStream of CIO.outstream
  33.  
  34.     exception NoSuchModule
  35.  
  36.   (** Trace Modules **)
  37.     datatype trace_module = TM of {
  38.     full_name : string,
  39.     label : string,
  40.     tracing : bool ref,
  41.     children : trace_module list ref
  42.       }
  43.  
  44.     val traceRoot = TM{
  45.         full_name = "/",
  46.         label = "/",
  47.         tracing = ref false,
  48.         children = ref []
  49.       }
  50.  
  51.     fun fullName "" = "/"
  52.       | fullName s = if (ordof(s, size s - 1) = (* "/" *)47) then s else s ^ "/"
  53.  
  54.     fun forAll f = let
  55.       fun for (tm as TM{children, ...}) = (f tm; forChildren(!children))
  56.       and forChildren [] = ()
  57.         | forChildren (tm::r) = (for tm; forChildren r)
  58.       in
  59.         for
  60.       end
  61.  
  62.     fun findTraceModule name = let
  63.       val n = size name
  64.       fun find (i, tm as TM{label, children, ...}) = let
  65.         val labelLen = size label
  66.         fun match j = if (j < labelLen)
  67.                 then if (ordof(label, j) = ordof(name, i+j))
  68.               then match(j+1)
  69.               else NONE
  70.               else if (i+j < n)
  71.             then findChild(i+j, !children)
  72.             else SOME tm
  73.         and findChild (i, []) = NONE
  74.           | findChild (i, c::r) = (case find(i, c)
  75.                of NONE => findChild(i, r)
  76.             | someTM => someTM
  77.               (* end case *))
  78.         in
  79.           if (i+labelLen > n)
  80.             then NONE
  81.             else match 0
  82.         end (* find *)
  83.       in
  84.         find(0, traceRoot)
  85.       end
  86.  
  87.     fun traceModule' (TM parent, name) = let
  88.       fun checkChildren [] = let
  89.         val tm = TM{
  90.                 full_name = implode[#full_name parent, name, "/"],
  91.                 label = name,
  92.             tracing = ref(!(#tracing parent)),
  93.                 children = ref []
  94.               }
  95.         in
  96.           (#children parent) := tm :: !(#children parent);
  97.           tm
  98.         end
  99.         | checkChildren((tm as TM{label, ...})::r) =
  100.         if (label = name) then tm else checkChildren r
  101.       in
  102.         checkChildren (! (#children parent))
  103.       end
  104.  
  105.   (* return the name of the module *)
  106.     fun nameOf (TM{full_name, ...}) = full_name
  107.  
  108.   (* return the module specified by the given string *)
  109.     fun moduleOf' name = (case findTraceModule(fullName name)
  110.            of NONE => raise NoSuchModule
  111.             | (SOME tm) => tm
  112.           (* end case *))
  113.  
  114.   (* turn tracing on for a module and its descendents *)
  115.     val traceOn' = forAll (fn (TM{tracing, ...}) => tracing := true)
  116.  
  117.   (* turn tracing off for a module and its descendents *)
  118.     val traceOff' = forAll (fn (TM{tracing, ...}) => tracing := false)
  119.  
  120.   (* turn tracing on for a module (but not for its descendents) *)
  121.     fun traceOnly' (TM{tracing, ...}) = tracing := true
  122.  
  123.   (* return true if this module is being traced *)
  124.     fun amTracing (TM{tracing, ...}) = !tracing
  125.  
  126.   (* return a list of the registered modules dominated by the given
  127.    * module, and their status.
  128.    *)
  129.     fun status' root = let
  130.       fun list (tm as TM{tracing, children, ...}, l) =
  131.         listChildren (!children, (tm, !tracing)::l)
  132.       and listChildren ([], l) = l
  133.         | listChildren (c::r, l) = listChildren(r, list(c, l))
  134.       in
  135.         rev (list (root, []))
  136.       end
  137.  
  138.   (** Trace printing **)
  139.     val traceDst = ref TraceToOut
  140.  
  141.     fun setTraceFile'  t = traceDst := t
  142.  
  143.     fun tracePrint s = let
  144.       fun output strm = (CIO.output(strm, s); CIO.flush_out strm)
  145.       in
  146.         case !traceDst
  147.          of TraceToOut => output CIO.std_out
  148.           | TraceToErr => output CIO.std_err
  149.           | TraceToNull => ()
  150.           | (TraceToFile fname) => let
  151.           val traceDst = (TraceToStream(CIO.open_out fname))
  152.                 handle _ => (
  153.               CMLBase.reportError(implode[
  154.                   "TraceCML: unable to open \"", fname,
  155.                   "\", redirecting to stdout"
  156.                 ]);
  157.               TraceToOut)
  158.           in
  159.             setTraceFile' traceDst;
  160.             tracePrint s
  161.           end
  162.             | (TraceToStream strm) => output strm
  163.         (* end case *)
  164.       end
  165.  
  166.   (** Trace server **)
  167.     val traceCh : (unit -> string list) chan = channel()
  168.     val traceUpdateCh : (unit -> unit) chan = channel()
  169.  
  170.     fun traceServer () = let
  171.       val evt = [
  172.           wrap(receive traceCh, fn f => tracePrint(implode(f()))),
  173.           wrap(receive traceUpdateCh, fn f => f())
  174.         ]
  175.       fun loop () = (select evt; loop())
  176.       in
  177.         loop()
  178.       end (* traceServer *)
  179.  
  180.     fun tracerStart () = (spawn traceServer; ())
  181.     fun tracerStop () = ()
  182.  
  183.     val _ = (
  184.       RunCML.logChannel ("TraceCML:trace", traceCh);
  185.       RunCML.logChannel ("TraceCML:trace-update", traceUpdateCh);
  186.       RunCML.logServer ("TraceCML:trace-server", tracerStart, tracerStop))
  187.  
  188.     local
  189.       fun carefully f = if CMLBase.isRunning() then send(traceUpdateCh, f) else f()
  190.       fun carefully' f = if CMLBase.isRunning()
  191.           then let
  192.             val reply = condVar()
  193.             in
  194.               send (traceUpdateCh, fn () => (writeVar(reply, f())));
  195.           readVar reply
  196.             end
  197.           else f()
  198.     in
  199.     fun traceModule arg = carefully' (fn () => traceModule' arg)
  200.     fun moduleOf name = carefully' (fn () => moduleOf' name)
  201.     fun traceOn tm = carefully (fn () => traceOn' tm)
  202.     fun traceOff tm = carefully (fn () => traceOff' tm)
  203.     fun traceOnly tm = carefully (fn () => traceOnly' tm)
  204.     fun setTraceFile f = carefully (fn () => setTraceFile' f)
  205.     fun status root = carefully' (fn () => status' root)
  206.     end (* local *)
  207.  
  208.     fun trace (TM{tracing, ...}, prFn) =
  209.       if (CMLBase.isRunning() andalso (!tracing))
  210.         then send(traceCh, prFn)
  211.         else ()
  212.  
  213.  
  214.   (** Thread watching **)
  215.  
  216.   (* controls printing of thread watching messages *)
  217.     val watcher = traceModule (traceRoot, "ThreadWatcher")
  218.     val _ = traceOn watcher
  219.  
  220.     datatype watcher_msg
  221.       = WATCH of (thread_id * unit cond_var)
  222.       | UNWATCH of thread_id
  223.  
  224.     val watcherCh : watcher_msg chan = channel ()
  225.  
  226.   (* watch the given thread for unexpected termination *)
  227.     fun watch (name, tid) = let
  228.       val cv = condVar()
  229.       fun handleTermination () = (
  230.         trace (watcher, fn () => [
  231.           "WARNING!  Watched thread ", name, tidToString tid, " has died.\n"
  232.           ]);
  233.         send (watcherCh, UNWATCH tid))
  234.       fun watcherThread () = (
  235.         send (watcherCh, WATCH(tid, cv));
  236.         select [
  237.             readVarEvt cv,
  238.             wrap (threadWait tid, handleTermination)
  239.           ])
  240.       in
  241.         spawn (watcherThread); ()
  242.       end
  243.  
  244.   (* stop watching the named thread *)
  245.     fun unwatch tid = send(watcherCh, UNWATCH tid)
  246.  
  247.   (* the watcher server *)
  248.     fun startWatcher () = let
  249.       fun remove (tid, watchedThreads) = let
  250.         fun look [] = []
  251.           | look ((id, cv)::r) = if sameThread(tid, id)
  252.               then (writeVar(cv, ()); r)
  253.               else ((id, cv) :: look r)
  254.         in
  255.           look watchedThreads
  256.         end
  257.       fun loop watchedThreads = (case (accept watcherCh)
  258.          of (WATCH(arg as (tid, _))) =>
  259.               loop (arg :: remove (tid, watchedThreads))
  260.           | (UNWATCH tid) => loop (remove (tid, watchedThreads))
  261.         (* end case *))
  262.       in
  263.         spawn (fn () => loop []); ()
  264.       end
  265.  
  266.     val _ = (
  267.       RunCML.logChannel ("TraceCML:watcherCh", watcherCh);
  268.       RunCML.logServer ("TraceCML:watcher-server", startWatcher, fn () => ()))
  269.  
  270.  
  271.   (** Uncaught exception handling **)
  272.  
  273.     fun defaultHandlerFn (tid, ex) = (
  274.       CMLBase.reportError (implode [
  275.           "uncaught exception ", System.exn_name ex, " in thread ",
  276.           CML.tidToString tid
  277.         ]))
  278.     val defaultHandler = ref defaultHandlerFn
  279.     val handlers = ref ([] : ((CML.thread_id * exn) -> bool) list)
  280.  
  281.   (* this sets the default uncaught exception action. *)
  282.     fun setUncaughtFn' action = defaultHandler := action
  283.  
  284.   (* add an additional uncaught exception action.  If the action returns
  285.    * true, then no further action is taken.  This can be used to handle
  286.    * handle application specific exceptions.
  287.    *)
  288.     fun setHandleFn' action = handlers := action :: !handlers
  289.  
  290.   (* this resets the default uncaught exception action to the system default,
  291.    * and removes any layered actions.
  292.    *)
  293.     fun resetUncaughtFn' () = (defaultHandler := defaultHandlerFn; handlers := [])
  294.  
  295.     val exnUpdateCh : (unit -> unit) chan = channel()
  296.  
  297.     fun exnServerStartup () = let
  298.       fun handleExn arg = let
  299.         val hdlrList = !handlers and dfltHndlr = !defaultHandler
  300.         fun loop [] = dfltHndlr arg
  301.           | loop (hdlr::r) = if (hdlr arg) then () else loop r
  302.         in
  303.           spawn (fn () => ((loop hdlrList) handle _ => (dfltHndlr arg)));
  304.           ()
  305.         end
  306.       val event = [
  307.           wrap (receive exnUpdateCh, fn f => f()),
  308.           wrap (receive errCh, handleExn)
  309.         ]
  310.       fun server () = (select event; server())
  311.       in
  312.         spawn server; ()
  313.       end
  314.  
  315.     val _ = (
  316.       RunCML.logChannel ("TraceCML:exnUpdateCh", exnUpdateCh);
  317.       RunCML.logServer ("TraceCML", exnServerStartup, fn () => ()))
  318.  
  319.     local
  320.       fun carefully f = if CMLBase.isRunning() then send(exnUpdateCh, f) else f()
  321.     in
  322.     fun setUncaughtFn arg = carefully (fn () => setUncaughtFn' arg)
  323.     fun setHandleFn arg = carefully (fn () => setHandleFn' arg)
  324.     fun resetUncaughtFn arg = carefully (fn () => resetUncaughtFn' arg)
  325.     end (* local *)
  326.  
  327.   end; (* TraceCML *)
  328.